using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using Beaker.OpenCube.Render.Maths;
using Beaker.OpenCube.Render.Sampling;

namespace Beaker.OpenCube.Render.Cameras
{
	public sealed class ViewPlane
	{
		private readonly int _pixelWidth;
		private readonly int _pixelHeight;
		private readonly ISampler _sampler;
		private readonly double _gamma;
		private readonly PixelIndexer _pixelIndexer;
		private readonly double _pixelSize;

		public ViewPlane( int pixelWidth, int pixelHeight, ISampler sampler, double gamma = 2.2d, double pixelSize = 1d )
		{
			Contract.Requires<ArgumentOutOfRangeException>( pixelWidth > 0 );
			Contract.Requires<ArgumentOutOfRangeException>( pixelHeight > 0 );
			Contract.Requires<ArgumentOutOfRangeException>( sampler != null );
			Contract.Requires<ArgumentOutOfRangeException>( gamma >= 0d );

			_pixelWidth = pixelWidth;
			_pixelHeight = pixelHeight;
			_sampler = sampler;
			_gamma = gamma;
			_pixelSize = pixelSize;

			_pixelIndexer = new PixelIndexer( _pixelWidth, _pixelHeight, gamma );
		}

		/// <summary>
		/// Gets the width in pixels.
		/// </summary>
		/// <value>
		/// The width in pixels.
		/// </value>
		[Pure]
		public int PixelWidth
		{
			
			get { return _pixelWidth; }
		}

		/// <summary>
		/// Gets the height in pixels.
		/// </summary>
		/// <value>
		/// The height in pixels.
		/// </value>
		[Pure]
		public int PixelHeight
		{
			
			get { return _pixelHeight; }
		}

		/// <summary>
		/// Gets the sampler
		/// </summary>
		/// <value>
		/// The sampler
		/// </value>
		[Pure]
		public ISampler Sampler
		{
			
			get { return _sampler; }
		}

		/// <summary>
		/// Gets the gamma.
		/// </summary>
		/// <value>
		/// The gamma.
		/// </value>
		[Pure]
		public double Gamma
		{
			
			get { return _gamma; }
		}

		/// <summary>
		/// Gets the size of the pixel.
		/// </summary>
		/// <value>
		/// The size of the pixel.
		/// </value>
		public double PixelSize
		{
			get { return _pixelSize; }
		}

		/// <summary>
		/// Gets the pixels.
		/// </summary>
		/// <value>
		/// The pixels.
		/// </value>
		[Pure]
		public PixelIndexer Pixels
		{
			
			get
			{
				return _pixelIndexer;
			}
		}

		public sealed class PixelIndexer
		{
			private readonly double _invGamma;
			private readonly Color[,] _pixels;

			public PixelIndexer( int width, int height, double gamma )
			{
				_pixels = new Color[width, height];
				_invGamma = 1d / gamma;
			}

			public Color this[int x, int y]
			{
				[Pure]
				get { return _pixels[x, y]; }
				set { _pixels[x, y] = value; }
			}

			public DisplayColor ConvertToDisplay(int x, int y)
			{
				Color c = _pixels[x, y];

				if( _invGamma != 1d )
					c = c.Power( _invGamma );

				return c.ToDisplayColor();
			}

			internal Color[,] Pixels
			{
				get { return _pixels; }
			}
		}

		public IEnumerable<Color> GetLine( int line )
		{
			// quickly create a copy of the line and return it
			Color[] lineBuffer = new Color[PixelWidth];
			for( int x = 0; x < PixelWidth; x++ )
				lineBuffer[x] = _pixelIndexer.Pixels[x, line];
			return lineBuffer;
		}
	}
}